package de.sendorian.app.forumArchive.dundjinni;


import static de.sendorian.util.matcher.FileTypeMatchers.*;
import static org.apache.commons.lang.StringUtils.*;
import static org.hamcrest.Matchers.*;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.log4j.Logger;
import org.hamcrest.Matcher;
import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.filters.HasAttributeFilter;
import org.htmlparser.filters.NotFilter;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.nodes.TagNode;
import org.htmlparser.util.NodeList;

import de.sendorian.app.forumArchive.Config;
import de.sendorian.app.forumArchive.domain.Archive;
import de.sendorian.app.forumArchive.domain.Image;
import de.sendorian.app.forumArchive.domain.Post;
import de.sendorian.app.forumArchive.finder.PostFinder;
import de.sendorian.util.htmlparser.LinkNameFilter;

public class DundjinniPostFinder extends PostFinder {

	private static final String FOOTER_INDICATOR = "__________________";

	private static Matcher<String> externalLinkMatcher = allOf(is(not(archive())),
	    not(startsWith(Config.get().getBaseUrl())), not(containsString("mailto")));


	public DundjinniPostFinder() {
		logger = Logger.getLogger(this.getClass().getName());
	}


	public DundjinniPostFinder(Matcher<Post> matcher) {
		super(matcher);
		logger = Logger.getLogger(this.getClass().getName());
	}


	@Override
	protected String getAuthor(TagNode tr) {
		NodeList tds = tr.getChildren().extractAllNodesThatMatch(new TagNameFilter("td"));
		if (tds.size() > 1) {
			TagNode td = (TagNode) tds.elementAt(0);
			NodeFilter filter = new HasAttributeFilter("class", "bold");
			NodeList spans = td.getChildren().extractAllNodesThatMatch(filter);
			if (spans.size() > 0) {
				return spans.elementAt(0).getFirstChild().getText();
			}
		}
		return null;
	}


	@Override
	protected String getMessage(TagNode tr) {
		NodeList messageNodes = getMessageNodes(tr);
		if (messageNodes != null && messageNodes.size() > 0) {
			String message = trimToEmpty(messageNodes.asString());
			if (message.contains(FOOTER_INDICATOR)) {
				message = message.substring(0, message.indexOf(FOOTER_INDICATOR));
			}
			return message;
		}
		return null;
	}


	@Override
	protected Date getPostDate(TagNode tr) {
		TagNode td = getSecondTd(tr);
		if (td != null) {
			NodeList tables = td.getChildren().extractAllNodesThatMatch(new TagNameFilter("table"));
			if (tables.size() > 0) {
				TagNode table = (TagNode) tables.elementAt(0);
				NodeList spans = new NodeList();
				table.collectInto(spans, new HasAttributeFilter("class", "smText"));
				if (spans.size() > 0) {
					String spanText = spans.elementAt(0).getFirstChild().getText();
					java.util.regex.Matcher matcher = Config.get().getDatePattern().matcher(
					    spanText);
					if (matcher.find()) {
						String date = matcher.group(1) + ' ' + matcher.group(2);
						try {
							return Config.get().getDateFormat().parse(date);
						} catch (ParseException e) {
							logger.error("Unable to parse the date '" + date + "'", e);
						}
					}
				}
			}
		}
		return null;
	}


	@Override
	protected String getPreviousPage(NodeList trs) {
		NodeList prevPage = trs.extractAllNodesThatMatch(new LinkNameFilter("&lt;&lt&nbsp;Prev"),
		    true);
		if (prevPage.size() == 1) {
			TagNode link = (TagNode) prevPage.elementAt(0);
			return Config.get().getBaseUrl() + link.getAttribute("href");
		}
		return null;
	}


	@Override
	protected List<Archive> getArchives(TagNode row) {
		List<Archive> archives = new ArrayList<Archive>();

		NodeList linkNodes = getLinksInMessageNodes(row);
		if (linkNodes != null && linkNodes.size() > 0) {
			for (Node node : linkNodes.toNodeArray()) {
				TagNode link = (TagNode) node;
				String linkTarget = link.getAttribute("href");
				if (archive().matches(linkTarget)) {
					Archive archive = new Archive();
					archive.setUrl(linkTarget);
					archives.add(archive);
				}
			}
		}
		return archives;
	}


	@Override
	protected List<Image> getImages(TagNode row) {
		List<Image> images = new ArrayList<Image>();
		Matcher<String> ignorableImageMatcher = new DundjinniIgnorableImageMatcher();

		NodeList messageNodes = getMessageNodes(row);
		if (messageNodes != null && messageNodes.size() > 0) {
			NodeList imageTags = messageNodes.extractAllNodesThatMatch(new TagNameFilter("img"),
			    true);
			for (Node node : imageTags.toNodeArray()) {
				TagNode imageTag = (TagNode) node;
				String url = imageTag.getAttribute("src");
				if (!ignorableImageMatcher.matches(url)) {
					Image image = new Image();
					image.setUrl(url);
					images.add(image);
				}
			}
		}
		return images;
	}


	@Override
	protected List<String> getExternalLinks(TagNode row) {
		List<String> externalLinks = new ArrayList<String>();

		NodeList linkNodes = getLinksInMessageNodes(row);
		if (linkNodes != null && linkNodes.size() > 0) {
			for (Node node : linkNodes.toNodeArray()) {
				TagNode link = (TagNode) node;
				String linkTarget = link.getAttribute("href");
				linkTarget = StringEscapeUtils.unescapeHtml(linkTarget);
				if (externalLinkMatcher.matches(linkTarget)) {
					externalLinks.add(linkTarget);
				}
			}
		}
		return externalLinks;
	}


	private TagNode getSecondTd(TagNode tr) {
		NodeList tds = tr.getChildren().extractAllNodesThatMatch(new TagNameFilter("td"));
		if (tds.size() > 1) {
			return (TagNode) tds.elementAt(1);
		}
		return null;
	}


	private NodeList getMessageNodes(TagNode row) {
		TagNode td = getSecondTd(row);
		if (td != null) {
			NodeFilter filter = new NotFilter(new TagNameFilter("table"));
			return td.getChildren().extractAllNodesThatMatch(filter);
		}
		return null;
	}


	private NodeList getLinksInMessageNodes(TagNode row) {
		NodeList messageNodes = getMessageNodes(row);
		if (messageNodes != null && messageNodes.size() > 0) {
			return messageNodes.extractAllNodesThatMatch(new TagNameFilter("a"), true);
		}
		return null;
	}

}
